import os,sys
from time import sleep
from string import split
from math import floor
from ctypes import *


class PulseBlaster:
    def __init__(self,test=False):
        self.isTest=test
        self.device_id=c_int()#create_string_buffer(4)
        self.board_no=c_int(0)
        self.base_no=c_int(2)
        self.fRet=c_int(0)
        self.ClockFrequency=c_double(400) #MHz
        self.PulseProgram=c_int(0)
        
        self.StartOpCode=c_int(2)
        self.ContinueOpCode=c_int(0)
        self.EndOpCode=c_int(3)
        
        #pulse=c_int(0xFFFFFF)
        #zero=c_int(0x000000)
        #pulseduration=c_double(5000.0)# ns
        #zeroduration=c_double(20000.0)# ns
        #self.NumberOfLoops=0# number
        
        self.dll=cdll.LoadLibrary("spinapi.dll")

    def ProgramPulse(self,n_loops,program):
        """
        parameters:
            - n_loops: number of loops
            - program:list of string [S1,S2,...Sn]
            Si = "xxx,yy..y"
            xxx: duration (in ns)
            yy..y: bits configuration, has to be between 0x0 and 0x1FFFFF, no more
            n>=2
        """
        
        # Number of loops
        self.NumberOfLoops = n_loops
        
        # Testing the format of 'program'
        flag_program_format_list=True
        flag_program_format_tuple=True
        flag_program_format_tuple_len=True
        flag_program_format_tuple_duration=True
        flag_program_format_tuple_channels=True
        flag_program=True
        if type(program) is not list:
            flag_program_format_list = False
        for i in range(len(program)):
            if type(program[i]) is not list:
                flag_program_format_tuple = False
            else:
                if len(program[i])!=2:
                    flag_program_format_tuple_len=False
                else:
                    if (type(program[i][0]) is not float) and (type(program[i][0]) is not int):
                        flag_program_format_tuple_duration=False
                    if type(program[i][1]) is not str:
                        flag_program_format_tuple_channels=False
                        
        # Display of error and flag_program
        if not flag_program_format_list:
            print "program is not a list."
            flag_program=False
        if not flag_program_format_tuple:
            print "At least one element of program is not a tuple."
            flag_program=False
        if not flag_program_format_tuple_len:
            print "At least one element of program is not a tuple of dimension 2."
            flag_program=False
        if not flag_program_format_tuple_duration:
            print "At least one tuple of program has not in first element a float or int."
            flag_program=False
        if not flag_program_format_tuple_channels:
            print "At least one tuple of program has not in second element a string."
            flag_program=False
        
        # Two lists creation: 
        # durations: list of durations in ns
        # channels: list of int corresponding to pulse flags
        if flag_program:
            self.durations=[]
            self.channels=[]
            n=len(program)
            for i in range(n):
                self.durations.append(float(program[i][0]))
                self.channels.append(int('E00000',16)+int(program[i][1],2)) # Somehow, we need the 3 highest weight bits to be 1
            
            
            if not self.isTest:
                self.dll.pb_stop()
                self.dll.pb_init()
                self.dll.pb_set_clock(self.ClockFrequency)
                self.dll.pb_start_programming(c_int(0)) # c_int ?
                for i in range(n):
                    if i==0: # first programming command
                        start = self.dll.pb_inst_pbonly(c_int(self.channels[i]), self.StartOpCode, c_int(self.NumberOfLoops), c_double(self.durations[i]))
                    elif i==n-1: # last programming command
                        self.dll.pb_inst_pbonly(c_int(self.channels[i]), self.EndOpCode, start, c_double(self.durations[i]))
                    else:
                        self.dll.pb_inst_pbonly(c_int(self.channels[i]), self.ContinueOpCode, c_int(0), c_double(self.durations[i]))
                self.dll.pb_stop_programming()
            else:
                print "Programming PulseBlaster"
                print "Durations: ",self.durations
                print "Channels: ",pulse.channels        
        
    def StartProgram(self):
        if not self.isTest:
            cdll.spinapi.pb_start()
            cdll.spinapi.pb_close()
        else:
            print "Pulse Start and Close"

if __name__ == "__main__":
    pulse=PulseBlaster(True)
    program=[[1000,"1111"],
             [1000,"0000"]]
    pulse.ProgramPulse(10000, program) # 20s of signal
    pulse.StartProgram()